/*************************************************************************//**
 * @file     tsi_cmd.c
 * @brief    MA35D1 TSI driver
 *
 * @copyright (C) 2023 Nuvoton Technology Corp. All rights reserved.
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "MA35D1.h"
#include "whc.h"
#include "tsi_cmd.h"

/** @addtogroup Standard_Driver Standard Driver
  @{
*/

/** @addtogroup TSI_Driver TSI Driver
  @{
*/

/** @addtogroup TSI_EXPORTED_FUNCTIONS TSI Exported Functions
  @{
*/

/// @cond HIDDEN_SYMBOLS

typedef struct err_code_t
{
	int    code;
	char   str[32];

}  ERR_CODE_T;

ERR_CODE_T  _err_code_tbl[] =
{
	ST_SUCCESS,               "ST_SUCCESS",
	ST_WAIT_TSI_SYNC,         "ST_WAIT_TSI_SYNC",
	ST_UNKNOWN_CMD,           "ST_UNKNOWN_CMD",
	ST_NO_TSI_IMAGE,          "ST_NO_TSI_IMAGE",
	ST_CMD_QUEUE_FULL,        "ST_CMD_QUEUE_FULL",
	ST_TIME_OUT,              "ST_TIME_OUT",
	ST_INVALID_PARAM,         "ST_INVALID_PARAM",
	ST_NO_AVAIL_SESSION,      "ST_NO_AVAIL_SESSION",
	ST_INVALID_SESSION_ID,    "ST_INVALID_SESSION_ID",
	ST_INVALID_OPERATION,     "ST_INVALID_OPERATION",
	ST_HW_NOT_READY,          "ST_HW_NOT_READY",
	ST_HW_ERROR,              "ST_HW_ERROR",
	ST_HW_BUSY,               "ST_HW_BUSY",
	ST_HW_TIME_OUT,           "ST_HW_TIME_OUT",
	ST_BUS_ERROR,             "ST_BUS_ERROR",
	ST_ECC_UNKNOWN_CURVE,     "ST_ECC_UNKNOWN_CURVE",
	ST_ECC_INVALID_PRIV_KEY,  "ST_ECC_INVALID_PRIV_KEY",
	ST_SIG_VERIFY_ERROR,      "ST_SIG_VERIFY_ERROR",
	ST_KS_READ_PROTECT,       "ST_KS_READ_PROTECT",
	ST_KS_FULL,               "ST_KS_FULL",
	ST_WHC_TX_BUSY,           "ST_WHC_TX_BUSY",
	ST_CMD_ACK_TIME_OUT,      "ST_CMD_ACK_TIME_OUT",
};

static volatile int  whc1_irq;

static void set_whc1_irq_flag(int val)
{
	isb();
	whc1_irq = val;
	isb();
}

static int get_whc1_irq_flag(void)
{
	isb();
	return whc1_irq;
}

static uint32_t get_time(void)
{
	return EL0_GetCurrentPhysicalValue() / 12000;
}

void WRHO1_IRQHandler(void)
{
	int  i;
	uint32_t  intsts;

	intsts = WHC1->INTSTS;

	if (intsts & (WHC_INTSTS_RX3IF_Msk | WHC_INTSTS_RX2IF_Msk |
		WHC_INTSTS_RX1IF_Msk | WHC_INTSTS_RX0IF_Msk))
	{
		set_whc1_irq_flag(1);
	}
	WHC1->INTSTS = 0x0f00003f;
}

int tsi_wait_ack(TSI_REQ_T *req, int time_out)
{
	int        i;
	uint64_t   t0;

	t0 = EL0_GetCurrentPhysicalValue();
	while (!get_whc1_irq_flag())
	{
		if (time_out && (EL0_GetCurrentPhysicalValue() - t0 > (time_out * 12000)))
		{
			/* command time-out, recall message */
			WHC1->TXCTL = (1<<(16+req->tx_channel));
			return ST_CMD_ACK_TIME_OUT;
		}
	}
	for (i = 0; i < 4; i++)
	{
		if (WHC1->RXSTS & (1 << i))         /* Check CHxRDY */
		{
			if ((WHC1->RMDAT[i][0] & TCK_CHR_MASK) == (req->cmd[0] & TCK_CHR_MASK))
			{
				req->ack[0] = WHC1->RMDAT[i][0];
				req->ack[1] = WHC1->RMDAT[i][1];
				req->ack[2] = WHC1->RMDAT[i][2];
				req->ack[3] = WHC1->RMDAT[i][3];
				WHC1->RXCTL = (1 << i);     /* set CHxACK */
				// sysprintf("[%d] ACK: 0x%x 0x%x 0x%x 0x%x\n", get_time(), req->ack[0], req->ack[1], req->ack[2], req->ack[3]);
				return 0;
			}
			else
			{
				req->ack[0] = WHC1->RMDAT[i][0];
				req->ack[1] = WHC1->RMDAT[i][1];
				req->ack[2] = WHC1->RMDAT[i][2];
				req->ack[3] = WHC1->RMDAT[i][3];
				// sysprintf("[%d] [%d] INVALID ACK: 0x%x 0x%x 0x%x 0x%x\n", get_time(), i, req->ack[0], req->ack[1], req->ack[2], req->ack[3]);
				WHC1->RXCTL = (1 << i);     /* set CHxACK */
				return 0;
			}
		}
	}
	sysprintf("tsi_wait_ack - error, irq occurred, but ack not received!!\n");
}

int tsi_send_command(TSI_REQ_T *req)
{
	int        i;

	for (i = 0; i < 4; i++)
	{
		if (WHC1->TXSTS & (1<<i))      /* Check CHxRDY */
			break;
	}
	if (i >= 4)
		return ST_WHC_TX_BUSY;         /* No WHC channel is ready for sending message */

	// sysprintf("[%d] CMD: 0x%x 0x%x 0x%x 0x%x\n", get_time(), req->cmd[0], req->cmd[1], req->cmd[2], req->cmd[3]);

	WHC1->TMDAT[i][0] = req->cmd[0];
	WHC1->TMDAT[i][1] = req->cmd[1];
	WHC1->TMDAT[i][2] = req->cmd[2];
	WHC1->TMDAT[i][3] = req->cmd[3];

	set_whc1_irq_flag(0);
	WHC1->TXCTL = (1 << i);            /* send message */
	req->tx_channel = i;
	req->tx_jiffy = EL0_GetCurrentPhysicalValue();
	return 0;
}

int tsi_send_command_and_wait(TSI_REQ_T *req, int time_out)
{
	int  ret;

	ret = tsi_send_command(req);
	if (ret != 0)
		return ret;

	ret = tsi_wait_ack(req, time_out);
	if (ret != 0)
		return ret;

	return TA_GET_STATUS(req);
}

/// @endcond HIDDEN_SYMBOLS

/**
  * @brief      Print human readable error messages to debug port.
  * @param[in]  code    TSI error code
  */
void TSI_Print_Error(int code)
{
	int   i;

	for (i = 0; i < sizeof(_err_code_tbl)/sizeof(ERR_CODE_T); i++)
	{
		if (_err_code_tbl[i].code == code)
		{
			sysprintf("  [%s]\n", _err_code_tbl[i].str);
			return;
		}
	}
	sysprintf("\nUnknow error code 0x%x!\n", code);
}

/**
  * @brief    Force TSI back to the initial state.
  * @return   0            success
  * @return   otherwise    error code from TSI
  */
int TSI_Sync(void)
{
	TSI_REQ_T  req;
	int        ret;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = CMD_TSI_SYNC << 16;
	return tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
}

/**
  * @brief    Get the version of TSI firmware.
  * @param[out]  ver_code     TSI firmware version code.
  * @return   0               success
  * @return   otherwise    error code from TSI
  */
int TSI_Get_Version(uint32_t *ver_code)
{
	TSI_REQ_T  req;
	int        ret;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = CMD_TSI_GET_VERSION << 16;
	ret = tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
	*ver_code = req.ack[1];
	return ret;
}

/**
  * @brief    Reset TSI immediately.
  * @return   0            success
  * @return   otherwise    error code from TSI
  */
int TSI_Reset(void)
{
	TSI_REQ_T  req;
	int        ret;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = CMD_TSI_RESET << 16;
	ret = tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
	return ret;
}

/**
  * @brief    Configure TSI UART port
  * @return   0            success
  * @return   otherwise    error code from TSI
  */
int TSI_Config_UART(uint32_t line, uint32_t baud)
{
	TSI_REQ_T  req;
	int        ret;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = CMD_TSI_CONFIG_UART<<16;
	req.cmd[1] = 0x11520087;
	req.cmd[2] = line;
	req.cmd[3] = baud;
	ret = tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
	return ret;
}


/**
  * @brief    Set TSI PLL clock. (MA35D1 SYS-PLL)
  * @param[in]  pllctl     The value to be written to TSI PLL_CTL register
  * @return   0            success
  * @return   otherwise    error code from TSI
  */
int TSI_Set_Clock(uint32_t pllctl)
{
	TSI_REQ_T  req;
	int        ret;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_TSI_SET_CLOCK << 16);
	req.cmd[1] = pllctl;

	ret = tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
	if (ret != 0)
		return ret;
	return 0;
}

/**
  * @brief    Load a patch image to TSI
  * @param[in]  base       Load address of the TSI image.
  * @param[in]  size       Size of the TSI image.
  * @return   0            success
  * @return   otherwise    error code from TSI
  */
int TSI_Load_Image(uint32_t base, uint32_t size)
{
	TSI_REQ_T  req;
	int        ret;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_TSI_LOAD_EX_FUNC << 16);
	req.cmd[1] = base;
	req.cmd[2] = size;
	ret = tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
	if (ret != 0)
		return ret;
	return 0;
}

/**
  * @brief    Request to monitor a memory block.
  * @param[in]  base      Base address of the memory block to minitor.
  * @param[in]  size      Byte count of the memory block to minitor.
  * @param[in]  interval  Monitor time interval in seconds.
  * @param[out] id        Monitor ID
  * @return   0            success
  * @return   otherwise    error code from TSI
  */
int TSI_Monitor_Set(uint32_t base, uint32_t size, int interval, int *id)
{
	TSI_REQ_T  req;
	int        ret;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_TSI_MONITOR_SET << 16);
	req.cmd[1] = interval & 0xffff;
	req.cmd[2] = base;
	req.cmd[3] = size;
	ret = tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
	if (ret != 0)
		return ret;
	*id = req.ack[1] & 0xff;
	return 0;
}

/**
  * @brief    Get TSI monitor area status
  * @param[in]  id         ID of the monitor.
  * @return   0            success
  * @return   otherwise    error code from TSI
  */
int TSI_Monitor_Status(int id)
{
	TSI_REQ_T  req;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_TSI_MONITOR_STATUS << 16) | (id & 0xff);
	return tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
}

/**
  * @brief    Close a TSI monitor process.
  * @param[in]  id         ID of the monitor.
  * @return   0            success
  * @return   otherwise    error code from TSI
  */
int TSI_Monitor_Close(int id)
{
	TSI_REQ_T  req;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_TSI_MONITOR_CLOSE << 16) | (id & 0xff);
	return tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
}

/**
  * @brief    Request an encrypt/decrypt session for AES or SHA.
  * @param[in]   class_code   The command class. Should be C_CODE_AES or C_CODE_SHA.
  * @param[out]  session_id   The session ID.
  * @return   0            success
  * @return   otherwise    error code from TSI
  */
int TSI_Open_Session(int class_code, int *session_id)
{
	TSI_REQ_T  req;
	int        ret;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_TSI_OPEN_SESSION << 16) | class_code;
	ret = tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
	if (ret != 0)
		return ret;
	*session_id = req.ack[1] & 0xff;
	return 0;
}

/**
  * @brief    Close an opened session.
  * @param[in]   class_code   The command class. Should be C_CODE_AES or C_CODE_SHA.
  * @param[in]   session_id   The session ID.
  * @return   0            success
  * @return   otherwise    error code from TSI
  */
int TSI_Close_Session(int class_code, int session_id)
{
	TSI_REQ_T  req;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_TSI_CLOSE_SESSION<<16) | (class_code<<8) | session_id;
	return tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
}


/**
  * @brief    TRNG init
  * @param[in]   method    0x0: Self-seeding. Seed is from TRNG noise
  *                        0x1: Nonce seeding. Seed is from noise and user provied nonce data,
  *                             which is put in parameter block and length is 48 words.
  *                        0x2: User seed. Seed is from user provided data, which is put in
  *                             parameter block and length is 12 words.
  * @param[in]   pb_addr   Address of parameter block. Not used if "method" is 0.
  *                        If "method" is 0x1, "param" should contains 48 words nounce data.
  *                        If "method" is 0x2, "param" should contains 12 words user defined seed.
  * @return   0            success
  * @return   otherwise    error code from TSI
  */
int TSI_TRNG_Init(int method, uint32_t pb_addr)
{
	TSI_REQ_T  req;
	int  ret;

	if ((method != 0) && (method != 1) && (method != 2))
		return ST_INVALID_PARAM;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_TRNG_INIT << 16) | method;
	req.cmd[1] = pb_addr;
	ret = tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
	return ret;
}

/**
  * @brief    Request TRNG to generate random numbers.
  * @param[in]  wcnt          Word count of random numbers
  * @param[in]  dest_addr     Destination address.
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int TSI_TRNG_Gen_Random(uint32_t wcnt, uint32_t dest_addr)
{
	TSI_REQ_T  req;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_TRNG_GEN_RANDOM << 16);
	req.cmd[2] = wcnt;
	req.cmd[3] = dest_addr;
	return tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
}


/**
  * @brief    PRNG re-seed
  * @param[in]   seed_src   Specify the source of PRNG seed
  *                         - 0:  Seed is generated from TSI TRNG.
  *                         - 1:  Use the "seed" as PRNG seed.
  * @param[in]   seed       PRNG seed
  * @return   0             success
  * @return   otherwise     error code from TSI
  */
int TSI_PRNG_ReSeed(int seed_src, uint32_t seed)
{
	TSI_REQ_T  req;
	int  ret;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_PRNG_RESEED << 16);
	req.cmd[1] = seed_src;
	req.cmd[2] = seed;
	ret = tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
	return ret;
}

/**
  * @brief    Request PRNG to generate a 64-bits random number.
  * @param[out]  rnd_w0       random number word 0
  * @param[out]  rnd_w1       random number word 1
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int TSI_PRNG_Gen_Random(uint32_t *rnd_w0, uint32_t *rnd_w1)
{
	TSI_REQ_T  req;
	int        ret;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_PRNG_GEN_RANDOM << 16);
	ret = tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
	*rnd_w0 = req.ack[1];
	*rnd_w1 = req.ack[2];
	return ret;
}

/**
  * @brief    Request PRNG to generate mass random numbers.
  * @param[in]  wcnt          Word count of random numbers
  * @param[in]  dest_addr     Destination address.
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int TSI_PRNG_Gen_Random_Mass(uint32_t wcnt, uint32_t dest_addr)
{
	TSI_REQ_T  req;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_PRNG_GEN_RAN_MASS << 16);
	req.cmd[2] = wcnt;
	req.cmd[3] = dest_addr;
	return tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
}


/**
  * @brief    Request PRNG to generate a random key to Key Store SRAM
  * @param[in]  owner       Owner of the random key.
  *                         0x0: Only for AES used
  *                         0x1: Only for HMAC used
  *                         0x4: Only for ECC used
  *                         0x5: Only for CPU used
  * @param[in]  is_ecdsa    1: Only for ECC ECDSA
  * @param[in]  is_ecdh     1: Only for ECC ECDH
  * @param[in]  keysz       Random key size
  *                             \ref KS_META_128
  *                             \ref KS_META_163
  *                             \ref KS_META_192
  *                             \ref KS_META_224
  *                             \ref KS_META_233
  *                             \ref KS_META_255
  *                             \ref KS_META_256
  *                             \ref KS_META_283
  *                             \ref KS_META_384
  *                             \ref KS_META_409
  *                             \ref KS_META_512
  *                             \ref KS_META_521
  *                             \ref KS_META_571
  *                             \ref KS_META_1024
  *                             \ref KS_META_2048
  *                             \ref KS_META_4096
  *                             \ref KS_META_READABLE
  *                             \ref KS_META_PRIV
  *                             \ref KS_META_NONPRIV
  *                             \ref KS_META_SECURE
  * @param[out]  key_num   Key Store KS_SRAM key number of the random key
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int TSI_PRNG_GenTo_KS_SRAM(uint32_t owner, int is_ecdsa, int is_ecdh, uint32_t keysz, int *key_num)
{
	TSI_REQ_T  req;
	int        ret;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_PRNG_GEN_KS_SRAM << 16);
	req.cmd[1] = (owner << CRYPTO_PRNG_KSCTL_OWNER_Pos) | (keysz >> KS_METADATA_SIZE_Pos);
	if (is_ecdh)
		req.cmd[1] |= CRYPTO_PRNG_KSCTL_ECDH_Msk;
	else if (is_ecdsa)
		req.cmd[1] |= CRYPTO_PRNG_KSCTL_ECDSA_Msk;

	ret = tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
	if (ret == 0)
		*key_num = req.ack[1];
	return 0;
}


/**
  * @brief    Configure AES encrypt/decrypt mode.
  * @param[in]  sid           The session ID obtained from TSI_Open_Session().
  * @param[in]  kinswap       1: Swap key and initial vector
  *                           0: MA35D1 not swap key and initial vector
  * @param[in]  koutswap      1: Swap feedback output
  *                           0: MA35D1 not swap AES feedback output
  * @param[in]  inswap        1: Swap input data
  *                           0: MA35D1 not swap input data
  * @param[in]  outswap       1: Swap output data
  *                           0: MA35D1 not swap output data
  * @param[in]  sm4en         1: Use SM4 cipher
  *                           0: Use AES cipher
  * @param[in]  encrypt       1: Execute encrypt operation
  *                           0: Execute decrypt operation
  * @param[in]  mode          Operation mode
  *                           - \ref AES_MODE_ECB
  *                           - \ref AES_MODE_CBC
  *                           - \ref AES_MODE_CFB
  *                           - \ref AES_MODE_OFB
  *                           - \ref AES_MODE_CTR
  *                           - \ref AES_MODE_CBC_CS1
  *                           - \ref AES_MODE_CBC_CS2
  *                           - \ref AES_MODE_CBC_CS3
  *                           - \ref AES_MODE_GCM
  *                           - \ref AES_MODE_GHASH
  *                           - \ref AES_MODE_CCM
  * @param[in]  keysz         Key size
  *                           - \ref AES_KEY_SIZE_128
  *                           - \ref AES_KEY_SIZE_192
  *                           - \ref AES_KEY_SIZE_256
  * @param[in]  ks            Key source
  *                           SEL_KEY_FROM_REG:      Key is assigned by AES_Set_Key command
  *                           SEL_KEY_FROM_KS_SRAM:  Key is from TSI Key Store SRAM
  *                           SEL_KEY_FROM_KS_OTP:   Key is from TSI Key Store OTP
  * @param[in]  ks_num        Key Store key number
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int TSI_AES_Set_Mode(int sid, int kinswap, int koutswap, int inswap, int outswap, int sm4en,
					 int encrypt, int mode, int keysz, int ks, int ks_num)
{
	TSI_REQ_T  req;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_AES_SET_MODE << 16) | sid;
	req.cmd[1] = (kinswap << 25) | (koutswap << 24) | (inswap << 23) | (outswap << 22) |
				 (sm4en << 17) | (encrypt << 16) | (mode << 8) | (keysz << 2);
	req.cmd[2] = (ks<<5) | ks_num;
	return tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
}

/**
  * @brief    Set AES/SM4 initial vector.
  * @param[in]  sid           The session ID obtained from TSI_Open_Session().
  * @param[in]  iv_addr       Address of the buffer for initial vector
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int TSI_AES_Set_IV(int sid, uint32_t iv_addr)
{
	TSI_REQ_T  req;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_AES_SET_IV << 16) | sid;
	req.cmd[1] = iv_addr;
	return tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
}

/**
  * @brief    Set AES/SM4 Keys.
  * @param[in]  sid           The session ID obtained from TSI_Open_Session().
  * @param[in]  keysz         Key size
  *                           - \ref AES_KEY_SIZE_128
  *                           - \ref AES_KEY_SIZE_192
  *                           - \ref AES_KEY_SIZE_256
  * @param[in]  key_addr       Address of the buffer for AES/SM4 key
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int TSI_AES_Set_Key(int sid, int keysz, uint32_t key_addr)
{
	TSI_REQ_T  req;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_AES_SET_KEY << 16) | sid;
	if (keysz == AES_KEY_SIZE_128)
		req.cmd[1] = 4;
	else if (keysz == AES_KEY_SIZE_192)
		req.cmd[1] = 6;
	else
		req.cmd[1] = 8;
	req.cmd[2] = key_addr;
	return tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
}

/**
  * @brief    Start AES encrypt/decrypt.
  * @param[in]  sid           The session ID obtained from TSI_Open_Session().
  * @param[in]  is_last       1: Is the last run of this AES/SM4 session.
  *                           0: Is not the last session.
  * @param[in]  data_cnt      AES/SM4 encrypt/decrypt data count in bytes
  *                           - \ref AES_KEY_SIZE_128
  *                           - \ref AES_KEY_SIZE_192
  *                           - \ref AES_KEY_SIZE_256
  * @param[in]  src_addr      DMA input data address
  * @param[in]  dest_addr     DMA output data address
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int TSI_AES_Run(int sid, int is_last, int data_cnt, uint32_t src_addr, uint32_t dest_addr)
{
	TSI_REQ_T  req;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_AES_RUN << 16) | sid;
	req.cmd[1] = (is_last << 24) | data_cnt;
	req.cmd[2] = src_addr;
	req.cmd[3] = dest_addr;
	return tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
}

/**
  * @brief    Start AES GCM mode encrypt/decrypt.
  * @param[in]  sid           The session ID obtained from TSI_Open_Session().
  * @param[in]  is_last       1: Is the last run of this AES/SM4 session.
  *                           0: Is not the last session.
  * @param[in]  data_cnt      AES/SM4 encrypt/decrypt data count in bytes
  *                           - \ref AES_KEY_SIZE_128
  *                           - \ref AES_KEY_SIZE_192
  *                           - \ref AES_KEY_SIZE_256
  * @param[in]  param_addr    Address of the parameter block
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int TSI_AES_GCM_Run(int sid, int is_last, int data_cnt, uint32_t param_addr)
{
	TSI_REQ_T  req;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_AES_GCM_RUN << 16) | sid;
	req.cmd[1] = (is_last << 24) | data_cnt;
	req.cmd[2] = param_addr;
	return tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
}

/**
  * @brief    Read or write AES/SM4 intermediate feedback data.
  * @param[in]  sid           The session ID obtained from TSI_Open_Session().
  * @param[in]  rw            1: write feedback data
  *                           0: read feedback data
  * @param[in]  wcnt          Word count of feedback data
  * @param[in]  fdbck_addr    Feedback data address
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int TSI_Access_Feedback(int sid, int rw, int wcnt, uint32_t fdbck_addr)
{
	TSI_REQ_T  req;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_AES_ACCESS_FEEDBACK << 16) | sid;
	req.cmd[1] = (rw<<7) | wcnt;
	req.cmd[2] = fdbck_addr;
	return tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
}

/**
  * @brief    Start to process the first block of a SHA session.
  * @param[in]  sid           The session ID obtained from TSI_Open_Session().
  * @param[in]  inswap        1: Swap input data
  *                           0: MA35D1 not swap input data
  * @param[in]  outswap       1: Swap output data
  *                           0: MA35D1 not swap output data
  * @param[in]  mode_sel      SHA engine mode
  *                           - \ref SHA_MODE_SEL_SHA1
  *                           - \ref SHA_MODE_SEL_SHA2
  *                           - \ref SHA_MODE_SEL_SHA3
  *                           - \ref SHA_MODE_SEL_SM3
  *                           - \ref SHA_MODE_SEL_MD5
  * @param[in]  hmac          1: Use HMAC key
  *                           0: No HMAC
  * @param[in]  mode          Operation mode
  *                           - \ref SHA_MODE_SHA1
  *                           - \ref SHA_MODE_SHA224
  *                           - \ref SHA_MODE_SHA256
  *                           - \ref SHA_MODE_SHA384
  *                           - \ref SHA_MODE_SHA512
  *                           - \ref SHA_MODE_SHAKE128
  *                           - \ref SHA_MODE_SHAKE256
  * @param[in]  keylen        HMAC key length in bytes. Only effective when "hmac" is 1.
  * @param[in]  ks            Key source
  *                           SEL_KEY_FROM_REG:      HMAC key is from TSI_SHA_Update() data
  *                           SEL_KEY_FROM_KS_SRAM:  HMAC key is from TSI Key Store SRAM
  *                           SEL_KEY_FROM_KS_OTP:   HMAC key is from TSI Key Store OTP
  * @param[in]  ks_num        Key Store key number
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int TSI_SHA_Start(int sid, int inswap, int outswap, int mode_sel, int hmac,
					 int mode, int keylen, int ks, int ks_num)
{
	TSI_REQ_T  req;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_SHA_START << 16) | sid;
	req.cmd[1] = (inswap << 23) | (outswap << 22) | (mode_sel << 12) |
				 (hmac << 11) | (mode << 8);
	req.cmd[2] = keylen;
	req.cmd[3] = (ks << 5) | ks_num;
	return tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
}

/**
  * @brief    Update SHA data.
  * @param[in]  sid           The session ID obtained from TSI_Open_Session().
  * @param[in]  data_cnt      byte count of input data
  * @param[in]  src_addr      Address of input data
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int TSI_SHA_Update(int sid, int data_cnt, uint32_t src_addr)
{
	TSI_REQ_T  req;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_SHA_UPDATE << 16) | sid;
	req.cmd[1] = data_cnt;
	req.cmd[2] = src_addr;
	return tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
}


/**
  * @brief    Update the last block of data and get result digest.
  * @param[in]  sid           The session ID obtained from TSI_Open_Session().
  * @param[in]  wcnt          Word count of output digest
  * @param[in]  data_cnt      Byte count of input data
  * @param[in]  src_addr      Address of input data
  * @param[in]  dest_addr     Address of output digest
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int TSI_SHA_Finish(int sid, int wcnt, int data_cnt, uint32_t src_addr, uint32_t dest_addr)
{
	TSI_REQ_T  req;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_SHA_FINISH << 16) | sid;
	req.cmd[1] = (wcnt << 24) | data_cnt;
	req.cmd[2] = src_addr;
	req.cmd[3] = dest_addr;
	return tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
}

/**
  * @brief    Run SHA all at once.
  * @param[in]  inswap        1: Swap input data
  *                           0: MA35D1 not swap input data
  * @param[in]  outswap       1: Swap output data
  *                           0: MA35D1 not swap output data
  * @param[in]  mode_sel      SHA engine mode
  *                           - \ref SHA_MODE_SEL_SHA1
  *                           - \ref SHA_MODE_SEL_SHA2
  *                           - \ref SHA_MODE_SEL_SHA3
  *                           - \ref SHA_MODE_SEL_SM3
  *                           - \ref SHA_MODE_SEL_MD5
  * @param[in]  mode          Operation mode
  *                           - \ref SHA_MODE_SHA1
  *                           - \ref SHA_MODE_SHA224
  *                           - \ref SHA_MODE_SHA256
  *                           - \ref SHA_MODE_SHA384
  *                           - \ref SHA_MODE_SHA512
  *                           - \ref SHA_MODE_SHAKE128
  *                           - \ref SHA_MODE_SHAKE256
  * @param[in]  wcnt          Word count of output digest
  * @param[in]  data_cnt      Byte count of input data
  * @param[in]  src_addr      Address of input data
  * @param[in]  dest_addr     Address of output digest
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int TSI_SHA_All_At_Once(int inswap, int outswap, int mode_sel, int mode,
						int wcnt, int data_cnt, uint32_t src_addr, uint32_t dest_addr)
{
	TSI_REQ_T  req;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_SHA_ALL_AT_ONCE << 16) | ((data_cnt >> 8) & 0xffff);
	req.cmd[1] = ((data_cnt & 0xff) << 24) | (inswap << 23) | (outswap << 22) |
				 (mode_sel << 12) | (mode << 8) | wcnt;
	req.cmd[2] = src_addr;
	req.cmd[3] = dest_addr;
	return tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
}

/**
  * @brief    Generate an ECC public key.
  * @param[in]  curve_id      ECC curve ID
  * @param[in]  is_ecdh       Only used when psel is ECC_KEY_SEL_KS_SRAM.
  *                           0: is not ECDH key.
  *                           1: is ECDH key.
  * @param[in]  psel          Select private key source
  *                           - \ref ECC_KEY_SEL_TRNG    : Private key is generated by TRNG
  *                           - \ref ECC_KEY_SEL_KS_OTP  : Private Key is from Key Store OTP
  *                           - \ref ECC_KEY_SEL_KS_SRAM : Private Key is from Key Store SRAM
  *                           - \ref ECC_KEY_SEL_USER    : User defined private key
  * @param[in]  d_knum        The Key Store key index. Effective only when "psel" is 0x01 or 0x02.
  * @param[in]  priv_key      Address of input private key. Effective only when "psel" is 0x03.
  * @param[in]  pub_key       Address of the output public key.
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int TSI_ECC_GenPublicKey(E_ECC_CURVE curve_id, int is_ecdh, int psel, int d_knum, uint32_t priv_key, uint32_t pub_key)
{
	TSI_REQ_T  req;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_ECC_GEN_PUB_KEY << 16) | curve_id;
	req.cmd[1] = (is_ecdh << 10) | (psel << 8) | d_knum;
	req.cmd[2] = priv_key;
	req.cmd[3] = pub_key;
	return tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
}

/**
  * @brief    Generate an ECC signature.
  * @param[in]  curve_id      ECC curve ID
  * @param[in]  rsel          0: Random number is generated by TSI TRNG
  *                           1: Use the random number specified in parameter block.
  * @param[in]  psel          Select private key source
  *                           - \ref ECC_KEY_SEL_TRNG    : Private key is generated by TRNG
  *                           - \ref ECC_KEY_SEL_KS_OTP  : Private Key is from Key Store OTP
  *                           - \ref ECC_KEY_SEL_KS_SRAM : Private Key is from Key Store SRAM
  *                           - \ref ECC_KEY_SEL_USER    : User defined private key
  * @param[in]  d_knum        The Key Store key index. Effective only when "psel" is 0x01 or 0x02.
  * @param[in]  param_addr    Address of the input parameter block, including message and private key.
  *                           The private key in parameter block is effective only when "psel" is 0x03.
  * @param[in]  sig_addr      Address of the output signature.
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int TSI_ECC_GenSignature(E_ECC_CURVE curve_id, int rsel, int psel, int d_knum, uint32_t param_addr, uint32_t sig_addr)
{
	TSI_REQ_T  req;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_ECC_GEN_SIG << 16) | curve_id;
	req.cmd[1] = (rsel << 10) | (psel << 8) | d_knum;
	req.cmd[2] = param_addr;
	req.cmd[3] = sig_addr;
	return tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
}

/**
  * @brief    Verify if an ECC signature valid or not.
  * @param[in]  curve_id      ECC curve ID
  * @param[in]  psel          Select public key source
  *                           - \ref ECC_KEY_SEL_KS_OTP  : Private Key is from Key Store OTP
  *                           - \ref ECC_KEY_SEL_KS_SRAM : Private Key is from Key Store SRAM
  *                           - \ref ECC_KEY_SEL_USER    : User defined private key
  * @param[in]  x_knum        The Key Store key number of public key X. Effective only when "psel" is 0x01 or 0x02.
  * @param[in]  y_knum        The Key Store key number of public key Y. Effective only when "psel" is 0x01 or 0x02.
  * @param[in]  param_addr    Address of the input parameter block.
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int TSI_ECC_VerifySignature(E_ECC_CURVE curve_id, int psel, int x_knum, int y_knum, uint32_t param_addr)
{
	TSI_REQ_T  req;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_ECC_VERIFY_SIG << 16) | curve_id;
	req.cmd[1] = (psel << 16) | (y_knum << 8) | x_knum;
	req.cmd[2] = param_addr;
	return tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
}

/**
  * @brief    Execute ECC point multiplication.
  * @param[in]  curve_id      ECC curve ID
  * @param[in]  type          Type of multipler k. 0x1: is ECDH key
  * @param[in]  msel          Select the source of multiplier
  *                           - 0x1: Multiplier is from Key Store OTP
  *                           - 0x2: Multiplier is from Key Store SRAM
  *                           - 0x3: Multiplier is from parameter block
  * @param[in]  sps           Select the source of input point
  *                           - 0x1: Input point is from Key Store OTP
  *                           - 0x2: Input point is from Key Store SRAM
  *                           - 0x3: Input point is from parameter block
  * @param[in]  m_knum        The Key Store key number of multiplier. Used only when "msel" is 0x01 or 0x02.
  * @param[in]  x_knum        The Key Store key number of input point X. Effective only when "msel" is 0x01 or 0x02.
  * @param[in]  y_knum        The Key Store key number of input point Y. Effective only when "msel" is 0x01 or 0x02.
  * @param[in]  param_addr    Address of the input parameter block.
  * @param[in]  dest_addr     Address of the output ECC point.
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int TSI_ECC_Multiply(E_ECC_CURVE curve_id, int type, int msel, int sps, int m_knum, int x_knum, int y_knum,
						uint32_t param_addr, uint32_t dest_addr)
{
	TSI_REQ_T  req;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_ECC_MULTIPLY << 16) | curve_id;
	req.cmd[1] = (type << 28) | (msel << 26) | (sps << 24) | (m_knum << 16) |
				 (x_knum << 8) | (y_knum);
	req.cmd[2] = param_addr;
	req.cmd[3] = dest_addr;
	return tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
}

/**
  * @brief    Execute RSA exponent modulus.
  * @param[in]  rsa_len       RSA bit length
  *                           - 0: 1024 bits
  *                           - 1: 2048 bits
  *                           - 2: 3072 bits
  *                           - 3: 4096 bits
  * @param[in]  crt           0: disable CRT; 1: enable CRT
  * @param[in]  esel          Select private key source
  *                           - \ref RSA_KEY_SEL_KS_OTP  : Exponent of exponentiation is from Key Store OTP
  *                           - \ref RSA_KEY_SEL_KS_SRAM : Exponent of exponentiation is from Key Store SRAM
  *                           - \ref RSA_KEY_SEL_USER    : Exponent of exponentiation is from input parameter block
  * @param[in]  e_knum        The Key Store key number of RSA exponent E. Used only when "esel" is RSA_KEY_SEL_KS_OTP or RSA_KEY_SEL_KS_SRAM.
  * @param[in]  param_addr    Address of the input parameter block.
  * @param[in]  dest_addr     Address of the output data.
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int TSI_RSA_Exp_Mod(int rsa_len, int crt, int esel, int e_knum, uint32_t param_addr, uint32_t dest_addr)
{
	TSI_REQ_T  req;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_EXT_RSA_EXP_MOD << 16) | rsa_len;
	req.cmd[1] = (crt<<10) | (esel<<8) | e_knum;
	req.cmd[2] = param_addr;
	req.cmd[3] = dest_addr;
	return tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
}

/**
  * @brief      Write key to key store SRAM
  * @param[in]  u32Meta     The metadata of the key. It could be the combine of
								\ref KS_META_AES
								\ref KS_META_HMAC
								\ref KS_META_RSA_EXP
								\ref KS_META_RSA_MID
								\ref KS_META_ECC
								\ref KS_META_CPU
								\ref KS_META_128
								\ref KS_META_163
								\ref KS_META_192
								\ref KS_META_224
								\ref KS_META_233
								\ref KS_META_255
								\ref KS_META_256
								\ref KS_META_283
								\ref KS_META_384
								\ref KS_META_409
								\ref KS_META_512
								\ref KS_META_521
								\ref KS_META_571
								\ref KS_META_1024
								\ref KS_META_2048
								\ref KS_META_4096
								\ref KS_META_READABLE
								\ref KS_META_PRIV
								\ref KS_META_NONPRIV
								\ref KS_META_SECURE
  * @param[out] au32Key       The buffer to store the key
  * @param[in]  iKeyNum       The SRAM key number which the key was written to
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int  TSI_KS_Write_SRAM(uint32_t u32Meta, uint32_t au32Key[], int *iKeyNum)
{
	TSI_REQ_T  req;
	int  ret;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_KS_WRITE_SRAM_KEY << 16);
	req.cmd[1] = u32Meta;
	req.cmd[2] = ptr_to_u32(au32Key);
	ret = tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
	*iKeyNum = req.ack[1];
	return ret;
}

/**
  * @brief      Write key to key store OTP
  * @param[in]  KeyNum       Key number of the OTP key to write
  * @param[in]  u32Meta      The metadata of the key. It could be the combine of
								\ref KS_META_AES
								\ref KS_META_HMAC
								\ref KS_META_RSA_EXP
								\ref KS_META_RSA_MID
								\ref KS_META_ECC
								\ref KS_META_CPU
								\ref KS_META_128
								\ref KS_META_163
								\ref KS_META_192
								\ref KS_META_224
								\ref KS_META_233
								\ref KS_META_255
								\ref KS_META_256
								\ref KS_META_283
								\ref KS_META_384
								\ref KS_META_409
								\ref KS_META_512
								\ref KS_META_521
								\ref KS_META_571
								\ref KS_META_1024
								\ref KS_META_2048
								\ref KS_META_4096
								\ref KS_META_READABLE
								\ref KS_META_PRIV
								\ref KS_META_NONPRIV
								\ref KS_META_SECURE
  * @param[out] au32Key       The buffer to store the key
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int  TSI_KS_Write_OTP(int KeyNum, uint32_t u32Meta, uint32_t au32Key[])
{
	TSI_REQ_T  req;
	int  ret;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_KS_WRITE_OTP_KEY << 16);
	req.cmd[1] = u32Meta;
	req.cmd[2] = ptr_to_u32(au32Key);
	req.cmd[3] = KeyNum;
	ret = tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
	return ret;
}

/**
  * @brief      Read key from key store
  * @param[in]  eType       The memory type. It could be:
							\ref KS_SRAM
							\ref KS_OTP
  * @param[in]  i32KeyIdx   The key index to read
  * @param[out] au32Key     The buffer to store the key
  * @param[in]  u32WordCnt  The word (32-bit) count of the key buffer size
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int  TSI_KS_Read(KS_MEM_Type eType, int32_t i32KeyIdx, uint32_t au32Key[], uint32_t u32WordCnt)
{
	TSI_REQ_T  req;
	int  ret;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_KS_READ_KEY << 16);
	req.cmd[1] = (eType << 30) | (u32WordCnt << 8) | i32KeyIdx;
	req.cmd[2] = ptr_to_u32(au32Key);
	ret = tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
	return ret;
}

/**
  * @brief      Revoke a key in key store
  * @param[in]  eType         The memory type. It could be:
							  \ref KS_SRAM
							  \ref KS_OTP
  * @param[in]  i32KeyIdx     The key index to read
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int  TSI_KS_RevokeKey(KS_MEM_Type eType, int32_t i32KeyIdx)
{
	TSI_REQ_T  req;
	int  ret;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_KS_REVOKE_KEY << 16);
	req.cmd[1] = (eType << 30) | i32KeyIdx;
	ret = tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
	return ret;
}

/**
  * @brief      Erase a key from key store
  * @param[in]    eType       The memory type. It could be:
							  \ref KS_SRAM
							  \ref KS_OTP
  * @param[in]  i32KeyIdx     The key index to erase
  * @return   0               success
  * @return   otherwise       error code from TSI
   */
int  TSI_KS_EraseKey(KS_MEM_Type eType, int32_t i32KeyIdx)
{
	TSI_REQ_T  req;
	int  ret;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_KS_ERASE_KEY << 16);
	req.cmd[1] = (eType << 30) | i32KeyIdx;
	ret = tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
	return ret;
}

/**
  * @brief    Erase all keys from Key Store SRAM
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int  TSI_KS_EraseAll(void)
{
	TSI_REQ_T  req;
	int  ret;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_KS_ERASE_ALL << 16);
	ret = tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
	return ret;
}

/**
  * @brief      Get remain size of Key Store SRAM
  * @param[in]  remain_size   Remain size of KS_SRAM
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int  TSI_KS_GetRemainSize(int *remain_size)
{
	TSI_REQ_T  req;
	int  ret;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_KS_REMAIN_SIZE << 16);
	ret = tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
	*remain_size = req.ack[1];
	return ret;
}

/**
  * @brief       Get status of Key Store
  * @param[out]  ks_sts       content of KS_STS register
  * @param[out]  ks_otpsts    content of KS_OTPSTS register
  * @param[out]  ks_metadata  content of KS_METADATA register
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int  TSI_KS_GetStatus(uint32_t *ks_sts, uint32_t *ks_otpsts, uint32_t *ks_metadata)
{
	TSI_REQ_T  req;
	int  ret;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_KS_GET_STATUS << 16);
	ret = tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
	*ks_sts = req.ack[1];
	*ks_otpsts = req.ack[2];
	*ks_metadata = req.ack[3];
	return ret;
}

/**
  * @brief      Read key data from OTP
  * @param[in]  u32Addr       The OTP address
  * @param[out] u32Data       The data read from OTP
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int  TSI_OTP_Read(uint32_t u32Addr, uint32_t *u32Data)
{
	TSI_REQ_T  req;
	int  ret;

	memset(&req, 0, sizeof(req));
	req.cmd[0] = (CMD_EXT_OTP_READ << 16);
	req.cmd[1] = u32Addr;
	ret = tsi_send_command_and_wait(&req, CMD_TIME_OUT_2S);
	*u32Data = req.ack[1];
	return ret;
}

/// @cond HIDDEN_SYMBOLS

__aligned(64) static unsigned char tsi_patch_image[] = {
	0x61, 0xc3, 0x66, 0xe4, 0x7a, 0x7a, 0x1b, 0x05, 0x2b, 0x43, 0x46, 0xc8, 0xe1, 0x40, 0x40, 0x74,
	0x9b, 0xdb, 0xb8, 0x0c, 0xd0, 0xab, 0x9a, 0x55, 0x0a, 0x5f, 0xfb, 0x2d, 0x96, 0xcb, 0x0a, 0xa1,
	0x6f, 0x1a, 0xaf, 0x9c, 0x0d, 0xf4, 0xd1, 0x77, 0xb8, 0x1e, 0x4a, 0xf5, 0x84, 0x08, 0x58, 0x26,
	0xa8, 0x18, 0xb9, 0xb9, 0x7a, 0xcf, 0x56, 0xe7, 0xe9, 0x2d, 0xa8, 0xb6, 0xce, 0x1c, 0x38, 0x99,
	0xfc, 0x9e, 0xfb, 0xf7, 0x64, 0x95, 0x4a, 0xf6, 0x80, 0xf2, 0x5b, 0x09, 0xe4, 0xde, 0xbe, 0x0c,
	0xa3, 0x86, 0x9f, 0x68, 0xf1, 0x2a, 0x6f, 0x92, 0x8d, 0x2b, 0xaf, 0x4d, 0xb8, 0xb7, 0xe8, 0x3a,
	0xff, 0x95, 0xd4, 0xf6, 0xd0, 0x41, 0x67, 0x3b, 0x17, 0x3c, 0xbe, 0x14, 0xac, 0x43, 0xfa, 0x3e,
	0x6d, 0x1f, 0x08, 0xc7, 0xe7, 0x88, 0x89, 0x89, 0x86, 0x7f, 0xf0, 0x9b, 0xc3, 0x19, 0xb2, 0x3e,
	0x21, 0x1a, 0x6d, 0x95, 0x35, 0xe1, 0x76, 0x25, 0x3e, 0xa8, 0x40, 0xee, 0x5e, 0x66, 0x00, 0x37,
	0x68, 0xaf, 0xff, 0xbd, 0x89, 0x1e, 0xd8, 0x3a, 0x98, 0x10, 0x89, 0x89, 0xb6, 0x37, 0x1a, 0xeb,
	0x6d, 0x82, 0xc7, 0xf4, 0xe3, 0x96, 0x2b, 0xe7, 0x4e, 0x31, 0x2c, 0xe2, 0x65, 0x19, 0x59, 0x99,
	0xbd, 0xf6, 0x57, 0xca, 0x02, 0x7e, 0x88, 0x70, 0x7b, 0xe8, 0x71, 0xd8, 0x6f, 0x33, 0x1b, 0x76,
	0x41, 0xb5, 0xed, 0xd4, 0x0c, 0x2f, 0xb3, 0x23, 0x15, 0xba, 0xe5, 0x68, 0xfc, 0x96, 0x17, 0xc8,
	0x01, 0x63, 0x7b, 0x0e, 0x84, 0xf9, 0xfa, 0x4a, 0xd1, 0x4c, 0xe3, 0xff, 0x63, 0xa1, 0xa4, 0x90,
	0x11, 0x8b, 0x53, 0x65, 0x32, 0x24, 0xff, 0x7a, 0x05, 0xe8, 0xf8, 0x0a, 0x87, 0xc6, 0x44, 0x7c,
	0x96, 0x22, 0xc8, 0xe8, 0x15, 0xd2, 0xb3, 0xa9, 0xc7, 0x1e, 0x57, 0x9d, 0xe1, 0x07, 0x6e, 0x34,
	0x6e, 0x4b, 0x36, 0xe0, 0x55, 0x08, 0x46, 0xe9, 0x3d, 0x02, 0x78, 0xef, 0x01, 0x0d, 0x9e, 0xfc,
	0xdd, 0xce, 0x2e, 0x41, 0x92, 0x32, 0x9b, 0x0d, 0x57, 0x4c, 0xb4, 0x2f, 0xf7, 0x80, 0x1a, 0x40,
	0xfa, 0xfc, 0x7b, 0xdc, 0xb6, 0xf3, 0x48, 0xbb, 0x0e, 0x01, 0x18, 0xce, 0x75, 0xf7, 0x10, 0x04,
	0x7b, 0x0e, 0x7f, 0x9b, 0x05, 0x3e, 0xf3, 0xf4, 0x77, 0xed, 0xf6, 0x97, 0x55, 0x2f, 0x71, 0x87,
	0x49, 0x33, 0xab, 0x41, 0xe6, 0x0e, 0x52, 0x69, 0x28, 0xce, 0xef, 0xed, 0xef, 0xdb, 0x34, 0xcf,
	0x96, 0xfb, 0xd8, 0x49, 0x6b, 0xf1, 0x80, 0xff, 0x91, 0x0e, 0xab, 0xc3, 0x2c, 0x70, 0x1e, 0x8c,
	0x0b, 0xe8, 0x24, 0x01, 0x6d, 0xce, 0x49, 0xe0, 0x4c, 0xae, 0x28, 0x7d, 0xde, 0x36, 0xc8, 0x02,
	0x42, 0x9a, 0x0e, 0x77, 0xa7, 0xa6, 0x3b, 0x59, 0x06, 0xf2, 0xd7, 0x69, 0x40, 0x7b, 0x0f, 0xb6,
	0x8d, 0x2a, 0xab, 0xc5, 0xe8, 0xd1, 0x46, 0x7f, 0x35, 0x6e, 0x4f, 0xfa, 0xad, 0xd9, 0x14, 0x26,
	0xf0, 0x0c, 0x7a, 0x97, 0x92, 0x31, 0x1c, 0x0f, 0x22, 0x6e, 0xb5, 0xf5, 0x39, 0xc1, 0xc7, 0x1a,
	0x59, 0x20, 0x92, 0xab, 0xeb, 0xc3, 0x29, 0xca, 0xe9, 0xcd, 0xa1, 0xa7, 0x59, 0xac, 0xde, 0xa0,
	0x57, 0xd2, 0xd2, 0x57, 0x26, 0x49, 0x63, 0x98, 0x1f, 0x76, 0xba, 0xb1, 0x2f, 0x68, 0x3c, 0x82,
	0xca, 0xcc, 0x30, 0x3a, 0xc6, 0xb0, 0xd8, 0xd8, 0xaf, 0x7d, 0xff, 0x36, 0x57, 0xa5, 0xd1, 0x42,
	0xeb, 0xa3, 0xb4, 0xc4, 0x40, 0x50, 0x8c, 0x3e, 0x35, 0x8e, 0x23, 0xb7, 0xf4, 0x43, 0xb9, 0x39,
	0x48, 0x1a, 0x79, 0x95, 0xa3, 0xd0, 0x03, 0xda, 0x4f, 0x09, 0x24, 0x9b, 0x31, 0xa4, 0xe5, 0x9d,
	0xc4, 0xbb, 0xf9, 0xb9, 0x2b, 0x64, 0x66, 0xaa, 0x10, 0x69, 0x9d, 0xb6, 0xc4, 0x1a, 0x02, 0x8e,
	0x07, 0x76, 0x06, 0x60, 0x9e, 0x33, 0xba, 0x5f, 0xfa, 0xef, 0x57, 0x38, 0x2e, 0x8c, 0x4a, 0x2b,
	0xcf, 0x1f, 0x3c, 0x31, 0x0b, 0x46, 0x63, 0xce, 0xe6, 0xbd, 0x18, 0x82, 0xa5, 0x3e, 0x0a, 0x05,
	0x23, 0xbe, 0xe6, 0xe7, 0xd6, 0xf9, 0xd8, 0x1a, 0x12, 0x0f, 0xad, 0x9c, 0x85, 0x76, 0xf4, 0xd0,
	0x27, 0x2b, 0x3b, 0xe5, 0xb6, 0x7a, 0xad, 0xd7, 0x77, 0x04, 0x77, 0xe2, 0x4a, 0xac, 0x42, 0xb3,
	0xa3, 0x2d, 0x98, 0xb9, 0x91, 0x97, 0xf0, 0xfe, 0x57, 0x11, 0xc8, 0xcd, 0xd8, 0x6e, 0x73, 0xb9,
	0x24, 0x9e, 0x22, 0x52, 0xc5, 0xd5, 0xa9, 0x07, 0xcd, 0x2e, 0x9c, 0xdd, 0xe8, 0x12, 0x91, 0xe1,
	0x2f, 0xcb, 0xeb, 0xdf, 0x7d, 0xb2, 0x62, 0x94, 0x3d, 0xa1, 0x1d, 0x37, 0x80, 0x22, 0x2e, 0x47,
	0xaf, 0xe5, 0xda, 0x0c, 0x2e, 0x42, 0x45, 0xd8, 0xeb, 0x83, 0xe6, 0x25, 0x46, 0x95, 0x50, 0xd8,
	0x10, 0xa5, 0x03, 0xe2, 0xd1, 0x79, 0xe6, 0x9b, 0x85, 0x31, 0x04, 0x39, 0x49, 0x0e, 0x5e, 0xda,
	0x1e, 0x7c, 0xed, 0x86, 0xe0, 0xe4, 0x84, 0x9e, 0x98, 0xf5, 0x3b, 0xe7, 0xd9, 0x55, 0xe2, 0x75,
	0xa0, 0x4c, 0xbd, 0xfa, 0x6e, 0xff, 0x13, 0x78, 0xbf, 0xa2, 0x64, 0xf3, 0x86, 0xcd, 0xb7, 0x0e,
	0xe1, 0x72, 0xf1, 0x14, 0xe4, 0xce, 0xcb, 0x45, 0xb1, 0x7f, 0xd9, 0x40, 0xd6, 0xd2, 0xbf, 0x65,
	0xb2, 0x5f, 0xf1, 0x51, 0xa2, 0x8f, 0x42, 0x2b, 0x9d, 0x46, 0xb4, 0x6d, 0xb1, 0xbf, 0x2c, 0xf9,
	0xc2, 0xf1, 0xb7, 0x9e, 0x81, 0xdd, 0x95, 0x7b, 0xcc, 0x7c, 0xf0, 0x18, 0xe6, 0xeb, 0xe4, 0xc8,
	0x59, 0x54, 0x7b, 0x9a, 0xb9, 0x43, 0x53, 0x2a, 0xa8, 0x6a, 0x7f, 0x03, 0xa0, 0xc8, 0xdd, 0xe6,
	0xa9, 0x32, 0x65, 0x53, 0x5d, 0x36, 0x0a, 0x11, 0xda, 0x51, 0x28, 0x5f, 0x02, 0x0b, 0x7b, 0xea,
	0x3a, 0xaa, 0xe6, 0x7c, 0x26, 0x74, 0xf5, 0x1c, 0x51, 0x56, 0xd6, 0xdc, 0xdf, 0x05, 0x07, 0xd7,
	0x6e, 0x3c, 0x6e, 0xf8, 0x22, 0x8d, 0x4e, 0x15, 0xb6, 0x14, 0xf6, 0x1c, 0xc4, 0x85, 0xce, 0x46,
	0xee, 0x42, 0xf2, 0x77, 0x92, 0x84, 0xf9, 0x4a, 0x9a, 0xa0, 0x0c, 0x73, 0xf2, 0xd6, 0xfb, 0x79,
	0x8a, 0xb8, 0x40, 0x58, 0xab, 0x35, 0x52, 0x0f, 0xb6, 0xaf, 0xa8, 0x75, 0x27, 0xda, 0x07, 0xdd,
	0xb6, 0x40, 0xcb, 0x95, 0x36, 0x98, 0xc8, 0xce, 0xa5, 0x2a, 0x69, 0x5d, 0x23, 0xfa, 0xee, 0xd1,
	0x7f, 0x63, 0x2d, 0x2e, 0xd2, 0x3e, 0x19, 0x21, 0x2e, 0xbb, 0xb4, 0x1d, 0x57, 0x37, 0xd4, 0x77,
	0x05, 0x19, 0x07, 0x35, 0xdd, 0x11, 0xc3, 0xa0, 0x48, 0x30, 0xc8, 0xee, 0xb8, 0x90, 0x2b, 0x1b,
	0xca, 0xb1, 0x47, 0xd7, 0xf9, 0x16, 0x7b, 0xb2, 0x54, 0x6d, 0x63, 0x35, 0x1c, 0x33, 0x18, 0xff,
	0x1d, 0xa3, 0xb0, 0x33, 0x15, 0x53, 0x95, 0xe3, 0x1f, 0xbe, 0x99, 0xa0, 0x97, 0xf9, 0xe3, 0x65,
	0x7a, 0x6e, 0x66, 0xe8, 0xe5, 0x72, 0x47, 0x00, 0x6f, 0xae, 0x96, 0xce, 0xce, 0x92, 0x28, 0x89,
	0x19, 0x4f, 0x52, 0x3f, 0x8b, 0x4c, 0xae, 0xdf, 0xec, 0xf0, 0x9f, 0x1f, 0x20, 0xed, 0x15, 0x86,
	0x04, 0x3e, 0xf0, 0x8f, 0x27, 0x5a, 0xdf, 0xd1, 0xf0, 0xcc, 0x45, 0xf5, 0x68, 0x41, 0x01, 0x95,
	0xb7, 0x3b, 0x27, 0xdb, 0xec, 0x3a, 0xc8, 0x41, 0xb0, 0xe9, 0xbd, 0x1f, 0x9c, 0x36, 0x54, 0x0a,
	0xcd, 0x23, 0xdd, 0x1e, 0x76, 0x8e, 0x8b, 0x49, 0x28, 0xf2, 0x51, 0xce, 0x44, 0x11, 0x36, 0xc0,
	0xeb, 0xe0, 0x33, 0x90, 0x9e, 0xd0, 0x8c, 0xb5, 0x8d, 0x68, 0x74, 0xc6, 0x97, 0x57, 0x8c, 0xed,
	0x10, 0x82, 0x18, 0x89, 0x74, 0x83, 0x6f, 0xe0, 0x42, 0x04, 0xa2, 0xdf, 0xa4, 0x66, 0x6d, 0xe1,
	0x1a, 0x79, 0x5a, 0x0d, 0xf2, 0xfb, 0x5f, 0x39, 0x95, 0x16, 0x0f, 0xc1, 0x5d, 0xfe, 0x70, 0x1d,
	0x14, 0x16, 0xdb, 0x16, 0xed, 0x39, 0x55, 0xb5, 0xa4, 0xe6, 0xce, 0x0e, 0xc1, 0x9d, 0x1b, 0xad,
	0xa2, 0xf5, 0xf6, 0x73, 0xca, 0x02, 0x9d, 0x27, 0x8f, 0x94, 0xfa, 0xca, 0x9f, 0x0d, 0x22, 0xc9,
	0xe6, 0xd9, 0x65, 0x1e, 0x18, 0x43, 0x5c, 0x35, 0xed, 0x3e, 0x5f, 0xab, 0x83, 0xba, 0xbf, 0x74,
	0x52, 0x4b, 0x39, 0x96, 0x96, 0x5e, 0xef, 0x2c, 0x17, 0x5c, 0xbb, 0x44, 0x9f, 0xdd, 0x11, 0x8e,
	0xbd, 0x43, 0x99, 0x96, 0xd9, 0x70, 0xdb, 0xa1, 0xc0, 0xfe, 0xe1, 0xca, 0x21, 0xa4, 0x5c, 0xde,
	0xbf, 0x44, 0x4c, 0x26, 0x1b, 0x0e, 0xad, 0x68, 0xff, 0x3f, 0x64, 0x82, 0x4e, 0xf7, 0xec, 0x0f,
	0x40, 0x63, 0x8e, 0x6a, 0x60, 0xe2, 0x4e, 0x37, 0xf3, 0xfb, 0x20, 0x1a, 0xcd, 0x68, 0xc9, 0x66,
	0x2c, 0x63, 0xa2, 0x69, 0x33, 0x41, 0xc1, 0xf5, 0x56, 0xc0, 0x75, 0x35, 0xc8, 0x24, 0xdd, 0xb3,
	0xb6, 0x23, 0x79, 0xac, 0xe8, 0x86, 0x56, 0xa7, 0x5c, 0x00, 0x2d, 0x83, 0x5a, 0x8c, 0x8c, 0xaa,
	0x7c, 0x02, 0xfc, 0x90, 0xa3, 0x19, 0x22, 0xd3, 0xca, 0x11, 0xcb, 0xef, 0xd3, 0x76, 0x6a, 0xb1,
	0xba, 0xb4, 0xff, 0x78, 0x4c, 0xf8, 0x06, 0xfd, 0xeb, 0x98, 0xeb, 0xcd, 0x34, 0x19, 0xdf, 0xde,
	0xbc, 0x46, 0x32, 0x5a, 0x7f, 0x77, 0xd1, 0xff, 0x76, 0x75, 0x8a, 0x1c, 0x69, 0xb4, 0xd5, 0xe0,
	0x3a, 0x70, 0x85, 0x67, 0xfe, 0x6b, 0x5e, 0x64, 0xff, 0x27, 0x1a, 0xa3, 0xca, 0x74, 0x2e, 0x58,
	0x63, 0x36, 0xa9, 0xf2, 0xd8, 0x0f, 0x62, 0xf7, 0x44, 0xde, 0xf6, 0x0b, 0x30, 0x61, 0x97, 0x3b,
	0xb7, 0x8f, 0x01, 0xa1, 0xf0, 0x13, 0x1c, 0x4a, 0x54, 0xf7, 0x4e, 0x34, 0xf2, 0xde, 0x6e, 0x16,
	0x89, 0x4c, 0x3b, 0xde, 0xf3, 0xbb, 0x42, 0xd9, 0x4f, 0xa3, 0x64, 0x4a, 0x95, 0xa3, 0x20, 0x9c,
	0x8f, 0x66, 0xdb, 0xea, 0x00, 0x0a, 0x75, 0x86, 0xf5, 0x52, 0xc2, 0x45, 0x1e, 0xe4, 0x19, 0xca,
	0xd2, 0xc9, 0x8e, 0xba, 0x47, 0xd9, 0xc2, 0x50, 0x19, 0x21, 0x0b, 0x31, 0x7f, 0xec, 0x6d, 0xee,
	0x5e, 0x6e, 0x7b, 0xa7, 0xe5, 0xc4, 0x95, 0xad, 0x3c, 0xec, 0xc9, 0xbe, 0x7e, 0x2c, 0x8a, 0x4a,
	0xe7, 0x1c, 0x31, 0x83, 0xc5, 0xbb, 0x0f, 0x09, 0xa8, 0xc7, 0xaf, 0x17, 0x16, 0x7e, 0xee, 0x29,
	0x13, 0x7d, 0xce, 0xad, 0xd6, 0x5d, 0x74, 0xdc, 0xbb, 0x60, 0x7a, 0x1e, 0xdb, 0xe6, 0x56, 0x31,
	0x45, 0x3c, 0xec, 0xd5, 0xf1, 0x6d, 0xed, 0x03, 0x3a, 0x60, 0xa2, 0xfe, 0x62, 0x5b, 0x09, 0xe4,
	0xa7, 0x0d, 0x5d, 0x5f, 0xe6, 0xc8, 0xfe, 0x91, 0x9b, 0xda, 0x9d, 0x27, 0x32, 0xc7, 0x4d, 0x90,
	0xf1, 0x2a, 0x04, 0x51, 0xb4, 0x2b, 0x43, 0xfa, 0x56, 0x72, 0x55, 0x6e, 0xbe, 0xad, 0x64, 0x62,
	0xe1, 0xcb, 0x92, 0x9a, 0x98, 0x79, 0xac, 0x68, 0x89, 0x73, 0x02, 0x61, 0x43, 0x9b, 0x5a, 0xcd,
	0x9b, 0xb2, 0xbe, 0x1f, 0x77, 0xf2, 0xc6, 0x32, 0xe1, 0xae, 0x89, 0x5f, 0x33, 0x7b, 0x98, 0x65,
	0xa1, 0x17, 0x42, 0x2a, 0x39, 0x62, 0xfc, 0xee, 0x70, 0xdd, 0x54, 0xce, 0x72, 0x23, 0xc2, 0xd2,
	0x13, 0xbb, 0xdf, 0x49, 0xd3, 0xdc, 0x6c, 0xe8, 0x31, 0x90, 0x58, 0x29, 0xc6, 0x52, 0xf5, 0x44,
	0x2c, 0x57, 0x85, 0x88, 0x64, 0x4d, 0x39, 0x91, 0xaf, 0x6b, 0x74, 0xb9, 0x60, 0x8d, 0xce, 0x1e,
	0x62, 0x3c, 0xc3, 0x6a, 0xf7, 0xcc, 0xb1, 0x66, 0x3e, 0x02, 0x88, 0xd3, 0x69, 0xef, 0xc6, 0xb7,
	0x64, 0x2b, 0x3b, 0x6d, 0xca, 0x52, 0xac, 0x8c, 0xa9, 0xbc, 0x65, 0x13, 0xbd, 0x39, 0x9b, 0x8f,
	0xe9, 0x25, 0xff, 0x73, 0x70, 0x8d, 0x3e, 0x65, 0x0f, 0x77, 0x63, 0x4e, 0x0f, 0xb5, 0x7a, 0xdd,
	0xec, 0x8a, 0xa0, 0x5d, 0xf0, 0xf3, 0x33, 0xe8, 0xb6, 0xfa, 0x71, 0x9c, 0xc5, 0x14, 0x22, 0x8d,
	0x2b, 0xb3, 0x4a, 0x31, 0x91, 0x6f, 0x4f, 0x5a, 0xbf, 0xd6, 0x42, 0x73, 0xd1, 0x45, 0x54, 0x7f,
	0x8c, 0x88, 0xa1, 0x4a, 0xcb, 0x67, 0x84, 0xe2, 0x28, 0xee, 0x71, 0x03, 0x05, 0x12, 0x79, 0xca,
	0x51, 0xa2, 0x78, 0xce, 0x41, 0x10, 0xa6, 0x2b, 0xf8, 0xa7, 0xac, 0x5d, 0x3e, 0x3f, 0xea, 0x48,
	0x8b, 0x19, 0xea, 0x0b, 0x11, 0x3e, 0xf0, 0x14, 0x26, 0xc2, 0x88, 0x4e, 0x5a, 0x53, 0x5c, 0x73,
	0x1a, 0x55, 0xe0, 0x2b, 0x43, 0x89, 0xed, 0x96, 0x8c, 0xc9, 0xd6, 0x41, 0xb5, 0x86, 0xdd, 0xcd,
	0x08, 0xc5, 0xff, 0x38, 0x64, 0x90, 0x9f, 0x79, 0xe3, 0x8c, 0xc2, 0x0d, 0x8b, 0x96, 0x6e, 0x6e,
	0x50, 0x79, 0x2a, 0x20, 0x01, 0xe7, 0x17, 0x89, 0xaf, 0x58, 0xbc, 0xf0, 0x9a, 0xc3, 0x77, 0x59,
	0x23, 0xc9, 0xd2, 0x5f, 0xe7, 0x2a, 0x62, 0x96, 0xa8, 0xc3, 0xd1, 0x65, 0xd5, 0x49, 0xc6, 0x5c,
	0xf3, 0x0f, 0xdb, 0xb4, 0x38, 0x81, 0x57, 0x5e, 0xb7, 0xf8, 0xc3, 0x44, 0xaf, 0x20, 0x7b, 0x40,
	0xfa, 0x9d, 0xb2, 0xc2, 0x82, 0x54, 0xdd, 0xdf, 0xd5, 0x9f, 0x9a, 0x3a, 0xcf, 0x73, 0x25, 0xd2,
	0xa3, 0x6d, 0x6e, 0xd6, 0x42, 0x0b, 0x3c, 0x75, 0x09, 0x69, 0x8e, 0x39, 0x40, 0x04, 0x00, 0xdd,
	0xc1, 0x22, 0x8b, 0x47, 0x4f, 0x53, 0xb8, 0x77, 0x70, 0xe6, 0xca, 0xf9, 0x8a, 0xeb, 0x62, 0x83,
	0x64, 0xa4, 0x13, 0x18, 0x3f, 0x9d, 0x09, 0x33, 0xd0, 0xd3, 0x3e, 0xb6, 0x7d, 0x8f, 0x4c, 0xf1,
	0x77, 0xf9, 0xa3, 0x99, 0x36, 0x2b, 0x99, 0x5d, 0xa1, 0x99, 0x13, 0x5e, 0xb2, 0xb7, 0x7e, 0x6f,
	0x4d, 0x5e, 0x1e, 0x3c, 0x42, 0xef, 0xb0, 0x66, 0x6d, 0x3b, 0xb5, 0xc7, 0x46, 0xae, 0x5d, 0x24,
	0x47, 0x9c, 0x6b, 0x59, 0x3c, 0x70, 0x55, 0xce, 0x1f, 0x24, 0x36, 0x8d, 0x86, 0x93, 0x45, 0x8e,
	0x0b, 0x95, 0xb6, 0x36, 0x9b, 0x4a, 0x5b, 0x0c, 0xc3, 0x75, 0x6f, 0xbe, 0xcf, 0x97, 0x61, 0x14,
	0x30, 0xf9, 0x3f, 0xf5, 0xf7, 0xe7, 0x7f, 0xd9, 0x47, 0x2e, 0xb6, 0xb2, 0x2c, 0x32, 0x96, 0xdc,
	0x28, 0x1a, 0xda, 0xdb, 0x52, 0xec, 0x54, 0xd4, 0xc1, 0x12, 0x2b, 0xb1, 0x7a, 0x40, 0xf0, 0x74,
	0x46, 0x26, 0xf1, 0x24, 0xc3, 0x9f, 0x6d, 0x79, 0x54, 0x07, 0xa3, 0x52, 0xe1, 0xb0, 0xf4, 0x4f,
	0xad, 0x16, 0xc9, 0xb5, 0x8f, 0xb3, 0x78, 0x71, 0xf9, 0x71, 0xa4, 0x56, 0xe5, 0xea, 0x92, 0x2b,
	0x92, 0x1e, 0x86, 0x9a, 0x8d, 0x3f, 0xff, 0xac, 0x4e, 0x0a, 0x44, 0xb5, 0x6e, 0xf6, 0x67, 0x34,
	0x68, 0x49, 0x4f, 0xd1, 0x88, 0x97, 0x5a, 0x4a, 0x98, 0x0d, 0xf6, 0x46, 0x27, 0x3d, 0xb5, 0x9e,
	0xf1, 0x7d, 0x65, 0x6f, 0x56, 0x3b, 0x7d, 0xcd, 0xbc, 0x53, 0xef, 0x1b, 0xf4, 0xb5, 0x8c, 0xc1,
	0xaf, 0x54, 0x3a, 0x00, 0x61, 0x64, 0x10, 0x28, 0xc4, 0x8d, 0x09, 0xe0, 0x7c, 0xc2, 0xd6, 0x22,
	0xcc, 0x93, 0xec, 0x7e, 0xcc, 0xb8, 0xd8, 0xca, 0x65, 0xbc, 0xc3, 0x89, 0x3d, 0x32, 0x58, 0xa7,
	0x60, 0x14, 0x42, 0x0e, 0xa8, 0x0c, 0xb4, 0x12, 0xfa, 0xf3, 0x81, 0x78, 0x27, 0xa8, 0x32, 0xc8,
	0x41, 0x15, 0xa1, 0x37, 0xd4, 0x39, 0x99, 0x2e, 0xa8, 0x5c, 0xc3, 0x6f, 0x03, 0xc6, 0x72, 0x3a,
	0x70, 0xc8, 0x8f, 0x1b, 0xbf, 0xaf, 0x11, 0xd1, 0xd6, 0xcd, 0x0e, 0x11, 0x9e, 0x90, 0xf0, 0x63,
	0xd5, 0xe9, 0xb2, 0xc1, 0x23, 0x90, 0xa0, 0x41, 0xd1, 0x10, 0x3c, 0x9d, 0x4c, 0xb1, 0x07, 0x9e,
	0x33, 0xa4, 0xe8, 0x06, 0x74, 0x16, 0x19, 0x8b, 0xa2, 0xf8, 0x21, 0xa4, 0xf2, 0xb3, 0x02, 0xd1,
	0x8e, 0x3f, 0xde, 0x45, 0x75, 0x92, 0x6d, 0x19, 0x74, 0x43, 0x78, 0xd3, 0xbc, 0x8e, 0x24, 0x27,
	0xba, 0x20, 0x3e, 0xc8, 0x3a, 0xec, 0xe7, 0x4a, 0x25, 0xb6, 0xa4, 0x90, 0x59, 0xa2, 0x7d, 0x10,
	0x63, 0xd3, 0x04, 0xef, 0xb9, 0x3a, 0x65, 0x69, 0xd8, 0x74, 0xdb, 0x2a, 0xbb, 0x03, 0x2e, 0x9c,
	0xbf, 0x84, 0x41, 0xf3, 0x0f, 0x39, 0xb6, 0x79, 0x3a, 0xe7, 0xbe, 0xee, 0xa7, 0x5c, 0x1d, 0x6b,
	0xf9, 0x92, 0xe1, 0xf9, 0x7b, 0xa7, 0x2c, 0x9d, 0x92, 0xd2, 0xd6, 0xc9, 0x31, 0x6a, 0x69, 0xf7,
	0xff, 0x43, 0xa1, 0x04, 0xee, 0x21, 0xf5, 0x71, 0x97, 0x30, 0x1a, 0xe1, 0x6e, 0x58, 0xcc, 0x22,
	0x53, 0x0d, 0x40, 0x82, 0xb7, 0xc1, 0xc3, 0x3b, 0xa6, 0x43, 0x9f, 0xfe, 0x1a, 0x1d, 0x50, 0xfa,
	0x2a, 0x9c, 0xd5, 0xd6, 0x36, 0x25, 0x1b, 0x37, 0x5f, 0x2f, 0xdc, 0x17, 0xe0, 0x33, 0xc7, 0x1f,
	0x67, 0x68, 0x28, 0x03, 0x4a, 0x0b, 0xc9, 0xb6, 0xa6, 0x70, 0x46, 0x37, 0x0f, 0xb7, 0xd5, 0xaa,
	0x54, 0x96, 0x49, 0x6b, 0x13, 0x58, 0x24, 0x94, 0x97, 0xa1, 0xee, 0xae, 0xb9, 0xcc, 0xbf, 0xb0,
	0xb8, 0xb3, 0x22, 0xe6, 0xb2, 0x29, 0xa0, 0xd5, 0x3e, 0xcf, 0xa0, 0xec, 0xf5, 0xee, 0x53, 0xe4,
	0xa9, 0xbf, 0xa2, 0x67, 0xdd, 0x27, 0xc3, 0x55, 0x0f, 0xff, 0xab, 0x69, 0x38, 0xcc, 0x4c, 0x09,
	0x0f, 0x72, 0xa8, 0xda, 0x85, 0x63, 0x30, 0x76, 0x1a, 0xcd, 0x76, 0x33, 0x42, 0x1c, 0xf2, 0x7b,
	0xb8, 0xf7, 0xbc, 0x84, 0x1c, 0x93, 0xc0, 0x25, 0x5c, 0xf6, 0x93, 0x49, 0x14, 0xda, 0x82, 0x68,
	0x3f, 0x32, 0x02, 0xcc, 0xf0, 0xc8, 0x42, 0x84, 0x36, 0x69, 0x4d, 0xa9, 0x23, 0x77, 0xd8, 0x38,
	0x17, 0x47, 0x2a, 0x11, 0xbd, 0x83, 0x31, 0x83, 0xa7, 0x3f, 0x06, 0x66, 0xf9, 0x41, 0x70, 0x2b,
	0xb7, 0xbe, 0x72, 0xaf, 0x89, 0x1c, 0x54, 0xe2, 0x0e, 0xe5, 0x53, 0xd2, 0x9e, 0x24, 0x83, 0x7e,
	0x6f, 0x3b, 0x30, 0x74, 0x66, 0x7a, 0x15, 0x3f, 0x02, 0x91, 0xc8, 0x29, 0xff, 0x03, 0x9e, 0x37,
	0x6d, 0x9b, 0x98, 0x04, 0x72, 0xbd, 0xac, 0x13, 0xda, 0x3b, 0x41, 0x31, 0x0b, 0x12, 0x96, 0xbb,
	0x1f, 0xe6, 0x9d, 0x54, 0x83, 0x7c, 0x5c, 0x07, 0x9b, 0x6e, 0xa6, 0x0a, 0x92, 0xa6, 0xd6, 0xab,
	0x04, 0x15, 0x7f, 0x89, 0x42, 0xdd, 0x26, 0x07, 0x1f, 0x96, 0x82, 0xf2, 0x20, 0x23, 0xd4, 0xbf,
	0xd1, 0x17, 0xc8, 0xae, 0x09, 0xf8, 0x41, 0xdc, 0xf5, 0x20, 0xbb, 0xd2, 0x61, 0x77, 0x6c, 0x4f,
	0xc5, 0xd1, 0x07, 0x8a, 0xf0, 0x19, 0x1f, 0xde, 0xaf, 0x00, 0x94, 0x70, 0xc8, 0xca, 0x0e, 0xb1,
	0x99, 0x9a, 0xa3, 0xc2, 0x58, 0x58, 0x74, 0x36, 0x88, 0x1a, 0x1e, 0xda, 0x23, 0x88, 0x96, 0x4b,
	0xd7, 0xc2, 0x2c, 0x1d, 0xe4, 0x7c, 0x5a, 0x43, 0xe9, 0x65, 0x68, 0xb6, 0x0d, 0x08, 0xe5, 0xe4,
	0x5e, 0x28, 0x86, 0x6b, 0xdf, 0x43, 0x12, 0xda, 0xd6, 0xa2, 0x10, 0xf7, 0xb0, 0x28, 0xd8, 0xef,
	0x4e, 0x5a, 0x98, 0x89, 0x60, 0xd8, 0x01, 0xa8, 0x11, 0x71, 0xb7, 0x02, 0xdd, 0xba, 0x82, 0x0d,
	0xdf, 0xe8, 0xb7, 0x4b, 0xaa, 0xae, 0x85, 0x77, 0x54, 0xc7, 0xb7, 0x84, 0x38, 0x34, 0x53, 0xb3,
	0x92, 0x52, 0xa7, 0xb8, 0xa9, 0xfe, 0xde, 0xc7, 0xc0, 0x6b, 0x1e, 0x19, 0x83, 0x35, 0x14, 0xd7,
	0x95, 0xb2, 0x55, 0xc8, 0xcf, 0x82, 0x50, 0xfc, 0x5a, 0x9f, 0xf1, 0xb3, 0x9b, 0xaf, 0xaa, 0x85,
	0x6e, 0xea, 0xfa, 0xb8, 0x28, 0xef, 0xe3, 0xd8, 0xd2, 0x9a, 0x94, 0x5a, 0x48, 0xfd, 0x2c, 0xd6,
	0x9e, 0xeb, 0x19, 0x8b, 0x0f, 0x10, 0xda, 0x51, 0xe4, 0x43, 0x9e, 0x3d, 0xbe, 0x12, 0x78, 0xf0,
	0x55, 0xa6, 0x50, 0x04, 0xe7, 0x0e, 0x8a, 0xfc, 0x4b, 0x93, 0x74, 0x3b, 0x6c, 0xd8, 0x40, 0x8d,
	0xd7, 0xb6, 0xc5, 0x97, 0xe2, 0x79, 0x53, 0x97, 0x65, 0x5e, 0xc6, 0x02, 0xb3, 0x18, 0x18, 0x9d,
	0x5b, 0xec, 0xdc, 0x2d, 0xec, 0xdd, 0xa6, 0x2c, 0x36, 0x86, 0x31, 0xce, 0x19, 0xf8, 0xae, 0xbf,
	0xa9, 0xfe, 0xe7, 0xd3, 0xe9, 0xe4, 0x64, 0xc6, 0x0f, 0x7e, 0xd9, 0xe6, 0xd0, 0xbd, 0x4c, 0x40,
	0x9b, 0xfe, 0x4e, 0xdb, 0x7a, 0x0e, 0x4f, 0xaa, 0xf0, 0xf7, 0x53, 0xaa, 0xa4, 0x66, 0x9d, 0x72,
	0x1e, 0x2c, 0x73, 0x97, 0x99, 0xa5, 0xeb, 0x47, 0xed, 0xb2, 0x7b, 0x6f, 0xa0, 0xb7, 0xc5, 0x6a,
	0xb1, 0xd0, 0xf9, 0xca, 0xc1, 0xd6, 0xe0, 0x4a, 0x9e, 0xfa, 0xf2, 0xb8, 0x4a, 0x31, 0x02, 0x74,
	0xf2, 0x9a, 0xa6, 0x45, 0x82, 0xe4, 0x48, 0xd2, 0xab, 0x3d, 0xf0, 0x1c, 0x10, 0xe3, 0x58, 0x4e,
	0x3c, 0x97, 0xbf, 0x9c, 0x7f, 0xea, 0x4e, 0x98, 0xb7, 0xc6, 0x39, 0x52, 0xf9, 0xe2, 0x22, 0xaf,
	0xc2, 0xfe, 0xac, 0x4f, 0x0b, 0x17, 0x05, 0x30, 0x9f, 0x5e, 0x6f, 0x25, 0x41, 0x7a, 0x3b, 0x6c,
	0x63, 0xc5, 0x48, 0xf4, 0x3b, 0xa3, 0x84, 0xe6, 0x8b, 0x68, 0xd4, 0x84, 0x22, 0x0e, 0x65, 0x57,
	0x16, 0x8f, 0xd8, 0x73, 0xa3, 0x0b, 0x0c, 0xef, 0x0a, 0x5a, 0xb0, 0x54, 0xe9, 0xdd, 0xbf, 0x4b,
	0xdc, 0x98, 0x9b, 0x13, 0x3c, 0x9a, 0xa3, 0xea, 0x8e, 0x26, 0xc9, 0x0e, 0x4d, 0x67, 0x65, 0x8c,
	0xec, 0x6d, 0x9e, 0xf9, 0xf0, 0x53, 0x38, 0xad, 0x91, 0x8c, 0x91, 0xe7, 0x54, 0xa2, 0xd3, 0x6f,
	0x7e, 0xfb, 0xfb, 0xcd, 0x5f, 0xd2, 0xfc, 0x43, 0xe1, 0x2a, 0xf8, 0x26, 0xfc, 0xe4, 0xcb, 0x03,
	0x19, 0x46, 0xb3, 0x35, 0xf2, 0x78, 0xde, 0x85, 0x2d, 0x2a, 0xdd, 0xa3, 0x71, 0x1b, 0xc9, 0xa9,
	0x60, 0xac, 0x4b, 0x21, 0x1a, 0x5d, 0x5c, 0xae, 0x58, 0xde, 0x3d, 0xb9, 0x29, 0x6c, 0x2c, 0x86,
	0x15, 0x88, 0xdb, 0xf6, 0xea, 0x46, 0xa1, 0x44, 0xaa, 0x27, 0xb8, 0xfa, 0xc3, 0xc5, 0xde, 0x9a,
	0xb7, 0x54, 0xaa, 0x03, 0x48, 0x5a, 0x98, 0xca, 0x18, 0xf7, 0xa3, 0x81, 0xa8, 0x09, 0x04, 0x9f,
	0x59, 0x02, 0x89, 0xca, 0x5e, 0xbc, 0xb2, 0x48, 0xc3, 0x2f, 0xbc, 0x97, 0x94, 0x28, 0x4b, 0xa0,
	0xa9, 0x41, 0x0a, 0x9f, 0x34, 0xfd, 0x61, 0x8b, 0xdb, 0x6b, 0xb7, 0x89, 0xa7, 0x5c, 0x71, 0x53,
	0x60, 0xca, 0x37, 0x47, 0x6f, 0x21, 0xdd, 0x00, 0x3c, 0x98, 0xe4, 0x04, 0x60, 0x0d, 0xa1, 0x5c,
	0xdc, 0x7a, 0x81, 0x97, 0x85, 0x0f, 0x0a, 0x30, 0x7c, 0xe2, 0x6b, 0xdf, 0x62, 0x13, 0x12, 0xdf,
	0x82, 0x28, 0x36, 0x9c, 0x6a, 0xaf, 0xd8, 0x04, 0x39, 0x09, 0xb7, 0xe1, 0xc1, 0x3b, 0xfe, 0x86,
	0x86, 0xa9, 0x09, 0x26, 0xc2, 0x41, 0x99, 0x0c, 0xbf, 0xd3, 0x1c, 0xe3, 0x67, 0xff, 0xa0, 0xae,
	0xda, 0xb4, 0x03, 0xc8, 0x93, 0xa0, 0x6c, 0xf0, 0xfc, 0xd9, 0x1c, 0xa1, 0x26, 0xc4, 0xb3, 0x4e,
	0x49, 0x3a, 0xc3, 0xb6, 0xf2, 0xeb, 0xd4, 0x9d, 0x7c, 0xdf, 0x56, 0xd7, 0x8c, 0x81, 0xdf, 0x84,
	0x95, 0x91, 0x70, 0xbd, 0x3f, 0xd9, 0x4a, 0xa0, 0x1e, 0xe2, 0x78, 0x6f, 0xa8, 0xa4, 0x88, 0x8a,
	0x48, 0xab, 0xf8, 0x08, 0x43, 0xdc, 0xab, 0xfb, 0xab, 0x2e, 0x9d, 0x1d, 0xb6, 0xfb, 0xbb, 0x8f,
	0xfc, 0x2d, 0x15, 0x5b, 0xa4, 0xd9, 0x51, 0xdb, 0xe5, 0x59, 0xe4, 0xfa, 0x0d, 0x87, 0xf6, 0x74,
	0xda, 0x66, 0x24, 0xa4, 0xc5, 0x9d, 0x8a, 0xf4, 0xf9, 0x9b, 0x95, 0x08, 0x23, 0xf8, 0xdb, 0x4d,
	0xca, 0xa0, 0xc8, 0x86, 0x85, 0x60, 0xdb, 0x94, 0x42, 0xe0, 0x91, 0x3b, 0x21, 0x11, 0x44, 0x0a,
	0xd7, 0x6b, 0xfa, 0xf6, 0xe3, 0x2f, 0xac, 0x1f, 0xe2, 0x0c, 0x5e, 0x47, 0x4d, 0x8e, 0xb8, 0xf9,
	0x06, 0x99, 0x9e, 0xe6, 0xdf, 0xa3, 0x30, 0x89, 0x16, 0xe7, 0x48, 0x38, 0xfd, 0xe9, 0xdc, 0xa6,
	0xef, 0x08, 0x48, 0xb5, 0xe4, 0x62, 0xb0, 0xa8, 0x1c, 0xfc, 0xdc, 0xea, 0x24, 0xb0, 0x8d, 0x25,
	0x66, 0xc7, 0x49, 0x41, 0x2c, 0x68, 0x33, 0xcf, 0xaf, 0x69, 0x2b, 0xcb, 0xfc, 0x46, 0x88, 0x7e,
	0x6d, 0x7d, 0xc7, 0x78, 0xd4, 0x80, 0x86, 0xba, 0x25, 0x76, 0x8d, 0x5b, 0xa9, 0xb9, 0xe0, 0xe7,
	0x70, 0xcc, 0xd1, 0xbc, 0x06, 0x97, 0xbe, 0x13, 0x3b, 0x8f, 0xf0, 0xe1, 0x3e, 0xa4, 0x0b, 0x6e,
	0x16, 0x7c, 0x71, 0xed, 0x8a, 0x38, 0xa0, 0xe3, 0x23, 0x40, 0x16, 0xe8, 0x7d, 0x5f, 0xe9, 0x03,
	0xfc, 0x18, 0x6e, 0x1b, 0x63, 0xb5, 0x14, 0xf0, 0x38, 0xb7, 0x53, 0x4a, 0x52, 0x42, 0x2f, 0x27,
	0x35, 0x33, 0xb5, 0x53, 0xc9, 0x4c, 0x9f, 0xac, 0xac, 0xf1, 0x0e, 0x50, 0xb5, 0xdd, 0xb5, 0xaf,
	0x56, 0x46, 0x94, 0x6a, 0xdd, 0x73, 0xc2, 0x7d, 0x41, 0x88, 0x0a, 0x1a, 0x22, 0x08, 0xf2, 0xf8,
	0x69, 0x0d, 0xab, 0x89, 0xac, 0x62, 0xf9, 0x70, 0xf6, 0x59, 0xf8, 0x7a, 0xfe, 0xee, 0x41, 0x94,
	0x7b, 0x5c, 0x66, 0x58, 0x5b, 0x24, 0xeb, 0x2b, 0xa3, 0x21, 0xe5, 0x8a, 0x04, 0xae, 0xf5, 0x8d,
	0xbd, 0x9b, 0xca, 0x7f, 0x54, 0xe5, 0x1e, 0xfc, 0x0c, 0xdf, 0xe4, 0x1a, 0x20, 0x25, 0x2a, 0x6d,
	0xef, 0x09, 0x5a, 0xac, 0x26, 0xf6, 0x2e, 0xc6, 0xc1, 0x11, 0x49, 0xdf, 0x8e, 0xa2, 0xa3, 0x69,
	0x75, 0x9b, 0xa8, 0xc1, 0x90, 0x01, 0xc8, 0x17, 0xb4, 0x0a, 0xe3, 0x70, 0xf4, 0xd5, 0xbe, 0x4f,
	0x54, 0xe2, 0xe3, 0xa1, 0xce, 0x8f, 0xa9, 0x46, 0x73, 0x5d, 0x9e, 0x1a, 0x96, 0x4e, 0x24, 0xaf,
	0x87, 0x4f, 0x74, 0x9b, 0x61, 0xc2, 0xcd, 0x36, 0xd5, 0x7c, 0xd4, 0xe2, 0x60, 0xe4, 0x20, 0x3c,
	0x8d, 0xfd, 0x10, 0xc8, 0x8d, 0x06, 0x26, 0xb1, 0xd2, 0x22, 0xc8, 0x01, 0x82, 0x37, 0x94, 0x90,
	0x69, 0x79, 0x2b, 0x8d, 0x1b, 0x13, 0x80, 0x80, 0x7f, 0xf5, 0xec, 0x0a, 0x5d, 0x3c, 0x8f, 0xfe,
	0x61, 0x2b, 0x0a, 0x31, 0xa6, 0xf1, 0x34, 0x93, 0x98, 0x70, 0x92, 0x49, 0x4e, 0xdc, 0xdc, 0xeb,
	0x37, 0xbb, 0x1b, 0xaa, 0xc0, 0xe5, 0xd1, 0x00, 0xaf, 0x2e, 0x35, 0x75, 0x58, 0xdf, 0xaf, 0x99,
	0xe2, 0x9c, 0xbe, 0x47, 0x1d, 0xa1, 0xba, 0x7b, 0x83, 0xad, 0xc4, 0x80, 0xf6, 0x80, 0xea, 0x3b,
	0x45, 0x0e, 0xb8, 0xd7, 0x45, 0x8f, 0x5f, 0xbc, 0x26, 0x48, 0x82, 0x75, 0x09, 0x2d, 0x5e, 0xe2,
	0x8f, 0x3d, 0xf4, 0x36, 0xcc, 0x52, 0x6a, 0x5d, 0x99, 0xa1, 0x69, 0x41, 0xf6, 0x58, 0x13, 0xc8,
	0xb5, 0x21, 0xb0, 0x4a, 0x42, 0x90, 0x1a, 0x46, 0xcc, 0x25, 0x83, 0x37, 0xe2, 0x42, 0x58, 0x48,
	0x45, 0x15, 0xc7, 0xb0, 0x58, 0xd7, 0x66, 0xf6, 0xab, 0xaf, 0xb0, 0x8e, 0x47, 0xfd, 0x96, 0xec,
	0x86, 0x2c, 0xb8, 0xda, 0xfc, 0x43, 0x25, 0x57, 0x44, 0xb5, 0x71, 0xb0, 0x10, 0x46, 0x3f, 0x3c,
	0x0c, 0x90, 0x6d, 0x61, 0x4a, 0xe3, 0x48, 0xe5, 0xb8, 0xf9, 0x09, 0xa7, 0xaf, 0x23, 0xf2, 0x91,
	0x40, 0x67, 0x48, 0xda, 0x9e, 0x31, 0x9b, 0x2d, 0x77, 0xa5, 0xe6, 0x58, 0x2e, 0x43, 0xe5, 0x4a,
	0x7f, 0x0c, 0x0a, 0xb0, 0xab, 0x66, 0x6e, 0xca, 0xae, 0x2e, 0x3b, 0x24, 0x7e, 0xa1, 0x7f, 0xc4,
	0x77, 0xd3, 0x8b, 0x90, 0xf8, 0x27, 0x35, 0xf2, 0xc6, 0x1b, 0x2d, 0x91, 0xeb, 0x0c, 0xa2, 0xd6,
	0xd8, 0xcd, 0x5c, 0x2f, 0x48, 0x41, 0x4c, 0xcc, 0xba, 0x7c, 0x04, 0x46, 0xc2, 0xbb, 0x5d, 0xda,
	0x14, 0xe6, 0x43, 0x6e, 0x30, 0x44, 0x10, 0x85, 0x49, 0xe4, 0xfe, 0x6a, 0xd6, 0x56, 0x0c, 0x72,
	0xf0, 0x42, 0xf8, 0x44, 0x74, 0xbf, 0x41, 0x35, 0x20, 0xff, 0x93, 0x40, 0xf8, 0xcf, 0x26, 0x57,
	0x7d, 0x32, 0x67, 0x51, 0x16, 0x1b, 0x6d, 0x81, 0x82, 0x08, 0xf1, 0x73, 0xdf, 0xc5, 0x7b, 0x4d,
	0xf7, 0x63, 0xeb, 0x08, 0xf8, 0xf9, 0xf5, 0x05, 0x00, 0x9d, 0x87, 0x29, 0x1f, 0x2f, 0x40, 0xfb,
	0x2a, 0xcd, 0x37, 0x2e, 0x8e, 0xba, 0x01, 0x96, 0x8f, 0x5c, 0x06, 0x9d, 0x8d, 0x0f, 0xf9, 0x9a,
	0x26, 0x0d, 0x63, 0x5f, 0xe7, 0x62, 0x25, 0x33, 0x0d, 0x75, 0x9b, 0x66, 0xa3, 0x9d, 0x53, 0xd2,
	0xdc, 0x3b, 0x3b, 0xb6, 0x42, 0x51, 0x94, 0xb0, 0xab, 0x71, 0x12, 0xb2, 0xa1, 0x17, 0xbd, 0xfe,
};

/// @endcond HIDDEN_SYMBOLS

/**
  * @brief    Initialize TSI command interface
  * @return   0               success
  * @return   otherwise       error code from TSI
  */
int TSI_Init(void)
{
	int ret;
	uint64_t t0, t1;

	set_whc1_irq_flag(0);
	IRQ_SetHandler(WRHO1_IRQn, WRHO1_IRQHandler);
	WHC1->INTSTS = 0xffffffff;
	IRQ_Enable(WRHO1_IRQn);
	WHC1->INTEN |= 0x0f00003f;   /* enable RX0~RX3 interrupt */

	t0 = EL0_GetCurrentPhysicalValue();
	while (TSI_Sync() != 0) {
		if (EL0_GetCurrentPhysicalValue() - t0 > 1200000)
			return ST_WAIT_TSI_SYNC;
	}

	TSI_Reset();

	t0 = EL0_GetCurrentPhysicalValue();
	while (TSI_Sync() != 0) {
		if (EL0_GetCurrentPhysicalValue() - t0 > 1200000) /* 100 ms timeout */
			return ST_WAIT_TSI_SYNC;
	}

	/* load patch image */
	ret = TSI_Load_Image(ptr_to_u32(tsi_patch_image), sizeof(tsi_patch_image));
	if (ret != 0)
	{
		sysprintf("[%d] TSI_Load_Image failed! - 0x%x\n", get_time(), ret);
		return ret;
	}

	return 0;
}

/*! @}*/ /* end of group TSI_EXPORTED_FUNCTIONS */

/*! @}*/ /* end of group TSI_Driver */

/*! @}*/ /* end of group Standard_Driver */
